package middleware

import (
	"context"
	"encoding/json"
	// "fmt"
	"net/http"
	"strconv"
	"strings"

	"mall/service/backend/common/enum/errorcode"
	"mall/service/backend/common/errorx"
	"mall/service/backend/common/i18n"

	"github.com/casbin/casbin/v2"
	// "github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/stores/redis"
	"github.com/zeromicro/go-zero/rest/httpx"
)

type AuthorityMiddleware struct {
	Cbn   *casbin.Enforcer
	Rds   *redis.Redis
	Trans *i18n.Translator
}

func NewAuthorityMiddleware(cbn *casbin.Enforcer, rds *redis.Redis, trans *i18n.Translator) *AuthorityMiddleware {
	return &AuthorityMiddleware{
		Cbn:   cbn,
		Rds:   rds,
		Trans: trans,
	}
}

func (m *AuthorityMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// logrus.Info(fmt.Sprintf("r.URL: %+v", r.URL))
		// logrus.Info(fmt.Sprintf("r.Method: %+v", r.Method))
		// logrus.Info(fmt.Sprintf("r raw: %+v", r))
		// Method  URL  Proto  ProtoMajor  ProtoMinor  Header[Accept  Accept-Encoding  Authorization  Cache-Control  Connection  Postman-Token  User-Agent]  Body[Reader]  GetBody  ContentLength  TransferEncoding  Close  Host  Form  PostForm  MultipartForm  Trailer  RemoteAddr  RequestURI  TLS  Cancel  Response  ctx]
		//get the path
		obj := r.URL.Path
		// logrus.Info(fmt.Sprintf("r.obj: %+v", obj))
		//get the method
		act := r.Method
		//get the role codes
		var roleCodes string
		if val := r.Context().Value("roles"); val != nil {
			roleCodes = val.(string)
		}
		var userId string
		if val := r.Context().Value("userId"); val != nil {
			if uid, err := val.(json.Number).Int64(); err == nil {
				userId = strconv.FormatInt(uid, 10)
			}
		}
		// logrus.Info(fmt.Sprintf("roleCodes: %s", roleCodes))
		// logrus.Info(fmt.Sprintf("userId: %s", userId))

		token := r.Header.Get("Authorization")
		// logrus.Info(fmt.Sprintf("token: %s", token))
		// logrus.Info(fmt.Sprintf("language: %s", r.Header.Get("Accept-Language")))

		//check jwt blacklist
		jwtResult, err := m.Rds.Get("token_" + token)
		if err != nil {
			logx.Errorw("redis error in jwt", logx.Field("detail", err.Error()))
			httpx.Error(w, errorx.NewApiError(http.StatusInternalServerError, err.Error()))
			return
		}

		if jwtResult == "1" {
			logx.Errorw("token in blacklist", logx.Field("detail", token))
			httpx.Error(w, errorx.NewApiErrorWithoutMsg(http.StatusUnauthorized))
			return
		}

		result := batchCheck(m.Cbn, roleCodes, act, obj)
		if result {
			logx.Infow("HTTP/HTTPS Request", logx.Field("UUID", userId),
				logx.Field("path", obj), logx.Field("method", act))

			//Passthrough to next handler if need
			next(w, r)
			return
		}

		logx.Errorw("the role is not permitted to access the API", logx.Field("roleCodes", roleCodes),
			logx.Field("path", obj), logx.Field("method", act))
		httpx.Error(w, errorx.NewCodeError(errorcode.PermissionDenied, m.Trans.Trans(
			context.WithValue(context.Background(), "lang", r.Header.Get("Accept-Language")),
			"common.permissionDeny")))
		return
	}
}

func batchCheck(cbn *casbin.Enforcer, roleCodes string, act string, obj string) bool {
	// return true
	var checkReq [][]any
	for _, v := range strings.Split(roleCodes, ",") {
		if v == "001" {
			return true
		}
		checkReq = append(checkReq, []any{v, obj, act})
	}

	result, err := cbn.BatchEnforce(checkReq)
	if err != nil {
		logx.Errorw("Casbin enforce error", logx.Field("detail", err.Error()))
		return false
	}

	for _, v := range result {
		if v {
			return true
		}
	}

	return false
}
